﻿using CMS.Controls.TabPageTemplate;
using CMS.Infos;
using CMS.REST;
using CMS.Util;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;

namespace CMS.Controls.DisplayTabPages.ChartTabPages
{
    public class ExportMonthChartTabPage : ChartExportTabPage
    {
        private DateTimePicker DTP_StartDate;
        private DateTimePicker DTP_EndDate;
        private ComboBox CB_Building;
        private MyNumericUpDown NUD_SplitDay;
        private TextBox TB_Path;


        protected override string MainButtonName => "开始导出";

        protected override string ExportName => "导出月报表";

        private static readonly string[] _SheetHead = new string[]
        {
            "序号",
            "日期",
            "课程名称",
            "实验名称",
            "指导老师",
            "班级",
            "人数",
            "学时",
            "人时数",
            "实验室"
        };
        public static readonly string[] Weeks = new string[] { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" };

        protected override void _InitFormPanel(FormPanel fp)
        {
            DTP_StartDate = new MyDateTimePicker();
            DTP_StartDate.ValueChanged += DTP_StartDate_ValueChanged;
            fp.AddField("开始统计日期", DTP_StartDate);

            DTP_EndDate = new MyDateTimePicker();
            DTP_EndDate.ValueChanged += DTP_EndDate_ValueChanged;
            fp.AddField("结束统计日期", DTP_EndDate);
            NUD_SplitDay = new MyNumericUpDown
            {
                Value = 20,
                Maximum = 31
            };
            fp.AddField("每月截止日期", NUD_SplitDay);
            SelectLastDate();

            FormBase.ColumnDefinition[] columns = FP_Main.CloneColumnDefinition(false, true, false);
            columns[1].Padding.Top = columns[1].Padding.Bottom = 15;

            Button Bn_LastMonth = new MyButton()
            {
                Text = "快速选择上月",
            };
            Bn_LastMonth.Click += (sender, e) => SelectLastDate();
            fp.AddField("快速填写", Bn_LastMonth, null, columns);

            CB_Building = new MyComboBox
            {
                DropDownStyle = ComboBoxStyle.DropDownList
            };
            RESTResponse response = RESTClient.HttpGet("apis/v1/building_names");
            if (response.IsSuccess)
            {
                CB_Building.Items.AddRange(response.Data.ToObject<string[]>());
            }
            CB_Building.SelectedItem = Utils.PrivateProfile.ReadString("ExportMonth", "Building", null);

            fp.AddField("导出教学楼", CB_Building);

            FolderBrowserDialog FBD_Main = new FolderBrowserDialog();
            TB_Path = new MyTextBox()
            {
                Text = Utils.PrivateProfile.ReadString("ExportMonth", "Path", ""),
            };
            fp.AddPathField("保存目录", TB_Path, FBD_Main);

        }

        private void DTP_EndDate_ValueChanged(object sender, EventArgs e)
        {
            if (DTP_EndDate.Value < DTP_StartDate.Value)
            {
                DTP_StartDate.Value = DTP_EndDate.Value;
            }
        }

        private void DTP_StartDate_ValueChanged(object sender, EventArgs e)
        {
            if (DTP_StartDate.Value > DTP_EndDate.Value)
            {
                DTP_EndDate.Value = DTP_StartDate.Value;
            }
        }

        private void SelectLastDate()
        {
            DateTime date = DateTime.Now.Date;
            if (date.Day <= NUD_SplitDay.Value)
            {
                //当前日期在交付日之前,最多到当前日期
                DTP_StartDate.Value = new DateTime(date.Month == 1 ? date.Year - 1 : date.Year, date.Month == 1 ? 12 : date.Month - 1, (int)NUD_SplitDay.Value);
                DTP_EndDate.Value = date;
            }
            else
            {
                //当前日期在交付日之后,最多到交付日
                DTP_StartDate.Value = new DateTime(date.Month == 1 ? date.Year - 1 : date.Year, date.Month == 1 ? 12 : date.Month - 1, (int)NUD_SplitDay.Value);
                DTP_EndDate.Value = new DateTime(date.Year, date.Month, (int)NUD_SplitDay.Value);
            }
        }

        private MonthChartRow[] ProcessData(DateTime startDate, DateTime endDate, string building, int splitDay)
        {
            TimeSpan halfYear = new TimeSpan(140, 0, 0, 0);
            _Publisher.WriteLine("INFO", "正在拉取学期数据");


            Term[] terms;
            RESTResponse response = RESTClient.IndexManaged("apis/v1/terms");
            if (response.IsSuccess)
            {
                terms = response.Data.ToObject<Term[]>();
            }
            else
            {
                _Publisher.WriteLine("ERROR", "无法拉取学期信息");
                return null;
            }

            terms = (from term in terms where term.StartDate != null && startDate <= term.StartDateRaw + halfYear && endDate >= term.StartDateRaw orderby term.StartDateRaw select term).ToArray();

            if (terms.Length == 0)
            {
                _Publisher.WriteLine("ERROR", "指定的区间内没有匹配到学期,请检查条件和学期信息");
                return null;
            }
            _Publisher.WriteLine("INFO", "正在拉取课室数据");

            Room[] rooms;
            response = RESTClient.IndexManaged("apis/v1/rooms", new RESTWhere("building", building));
            if (response.IsSuccess)
            {
                rooms = response.Data.ToObject<Room[]>();
            }
            else
            {
                _Publisher.WriteLine("ERROR", "无法拉取课室信息");
                return null;
            }

            _Publisher.WriteLine("INFO", "正在拉取课程数据");
            CourseRoom[] courses;
            response = RESTClient.IndexManaged("apis/v1/courses_room", new RESTWhere("term_id", "in", from term in terms select term.ID));
            if (response.IsSuccess)
            {
                courses = response.Data.ToObject<CourseRoom[]>();
            }
            else
            {
                _Publisher.WriteLine("ERROR", "无法拉取课室课程信息");
                return null;
            }

            Dictionary<DateTime, DateTime?> adjustMap = new Dictionary<DateTime, DateTime?>();
            response = RESTClient.HttpGetManaged("apis/v1/course_adjust_map");
            if (response.IsSuccess)
            {
                foreach (string[] i in response.Data.ToObject<string[][]>())
                {
                    if (string.IsNullOrEmpty(i[0]))
                    {
                        if (!string.IsNullOrEmpty(i[1]))
                        {
                            adjustMap[DateTime.Parse(i[1])] = null;
                        }
                    }
                    else
                    {
                        if (string.IsNullOrEmpty(i[1]))
                        {
                            adjustMap[DateTime.Parse(i[0])] = null;
                        }
                        else
                        {
                            adjustMap[DateTime.Parse(i[0])] = DateTime.Parse(i[1]);
                        }
                    }
                }
            }


            List<MonthChartRow> list = new List<MonthChartRow>();

            DateTime curDate = startDate;
            int curWeek = 0;
            int curClassWeek = 0;

            _Publisher.WriteLine("INFO", "正在数据统计数据");
            foreach (Term term in from t in terms orderby t.StartDateRaw select t)
            {
                if (curDate < term.StartDateRaw)
                {
                    curDate = term.StartDateRaw;
                }
                else if (curDate > term.StartDateRaw + halfYear)
                {
                    continue;
                }
                curWeek = TrimWeekDay(curDate.DayOfWeek);
                DateTime maxDate = endDate <= term.StartDateRaw + halfYear ? endDate : term.StartDateRaw + halfYear;
                while (curWeek < 20 && curDate < maxDate)
                {
                    if (adjustMap.TryGetValue(curDate, out DateTime? curDate2))
                    {
                        if (curDate2 == null)
                        {
                            curDate = curDate.AddDays(1);
                            curWeek = TrimWeekDay(curDate.DayOfWeek);
                            continue;
                        }
                        else
                        {
                            curWeek = TrimWeekDay(curDate2.Value.DayOfWeek);
                        }
                    }
                    else
                    {
                        curDate2 = curDate;
                    }
                    curClassWeek = 1 << ((int)(curDate2.Value - term.StartDateRaw).TotalDays / 7);
                    IEnumerable<MonthChartRow> filtered =
                        from course in courses
                        where term.ID == course.TermID && course.Week == curWeek
                          && (course.ClassWeekRaw & curClassWeek) != 0
                        let r = rooms.Where(room => room.Name == course.Room)
                        where r.Count() > 0
                        orderby course.Room
                        select new MonthChartRow()
                        {
                            Date = curDate,
                            ClassName = course.ClassName,
                            Teacher = course.Teacher,
                            TimeSpan = course.TimeSpan,
                            Room = course.Room,
                            Name = course.Name,
                            StudentCount = course.StudentCount == 0 ? r.First().SeatCount : course.StudentCount
                        };
                    if (filtered.Count() > 0)
                    {
                        list.AddRange(filtered);
                    }

                    curDate = curDate.AddDays(1);
                    curWeek = TrimWeekDay(curDate.DayOfWeek);
                }
            }


            return list.ToArray();
        }

        private static int TrimWeekDay(DayOfWeek week)
        {
            int _week = (int)week;
            return _week == 0 ? 7 : _week;
        }

        private DateTime GetYearMonth(DateTime dateTime, int splitDay)
        {
            if (dateTime.Day >= splitDay)
            {
                return new DateTime(dateTime.Month == 12 ? dateTime.Year + 1 : dateTime.Year, dateTime.Month % 12 + 1, 1); ;
            }
            else
            {
                return new DateTime(dateTime.Year, dateTime.Month, 1);
            }
        }

        protected override void StartExport()
        {
            if (!Directory.Exists(TB_Path.Text))
            {
                _Publisher.WriteLine("ERROR", "必须选择有效的保存路径");
                return;
            }

            if (CB_Building.SelectedIndex == -1)
            {
                _Publisher.WriteLine("ERROR", "必须选择一个教学楼进行导出");
                return;
            }

            Utils.PrivateProfile.WriteString("ExportMonth", "Path", TB_Path.Text);
            Utils.PrivateProfile.WriteString("ExportMonth", "Building", (string)CB_Building.SelectedItem);

            MonthChartRow[] data = ProcessData(DTP_StartDate.Value, DTP_EndDate.Value, (string)CB_Building.SelectedItem, (int)NUD_SplitDay.Value);
            string file = Path.Combine(TB_Path.Text, DateTime.Now.ToString("月报表yyMMddHHmmss") + ".xls");

            if (SaveToFile(file, data, (int)NUD_SplitDay.Value))
            {
                _Publisher.WriteLine("INFO", "文件已经导出到:" + file);
            }
        }


        private bool SaveToFile(string file, MonthChartRow[] rows, int splitDay)
        {
            IWorkbook workbook;
            if (string.Compare(Path.GetExtension(file), ".xls", true) == 0)
            {
                workbook = new HSSFWorkbook();
            }
            else
            {
                workbook = new XSSFWorkbook();
            }

            ISheet tmpSheet = workbook.CreateSheet("课程安排表");

            //初始化表头

            IFont tmpFont = workbook.CreateFont();
            tmpFont.FontHeightInPoints = 18;
            tmpFont.IsBold = true;
            ICellStyle tmpStyle = workbook.CreateCellStyle();
            tmpStyle.VerticalAlignment = VerticalAlignment.Center;
            tmpStyle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center;
            tmpStyle.SetFont(tmpFont);


            tmpSheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, 9));
            IRow tmpRow = tmpSheet.CreateRow(0);
            ICell tmpCell = tmpRow.CreateCell(0);
            tmpCell.CellStyle = tmpStyle;
            tmpCell.SetCellValue("课程安排表");


            tmpStyle = workbook.CreateCellStyle();
            tmpStyle.VerticalAlignment = VerticalAlignment.Center;
            tmpStyle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center;

            tmpRow = tmpSheet.CreateRow(1);
            for (int i = 0; i < 10; i++)
            {
                tmpCell = tmpRow.CreateCell(i);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(_SheetHead[i]);
            }

            int lines = 2;
            foreach (MonthChartRow row in rows)
            {
                tmpRow = tmpSheet.CreateRow(lines);
                tmpCell = tmpRow.CreateCell(0);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(lines - 1);

                tmpCell = tmpRow.CreateCell(1);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(row.Date.ToShortDateString());

                tmpCell = tmpRow.CreateCell(2);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(row.Name);

                tmpCell = tmpRow.CreateCell(3);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue("");

                tmpCell = tmpRow.CreateCell(4);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(row.Teacher);

                tmpCell = tmpRow.CreateCell(5);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(row.ClassName);

                tmpCell = tmpRow.CreateCell(6);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(row.StudentCount);

                tmpCell = tmpRow.CreateCell(7);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(row.TimeSpan);

                tmpCell = tmpRow.CreateCell(8);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(row.StudentCount * row.TimeSpan);

                tmpCell = tmpRow.CreateCell(9);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(row.Room);
                lines++;
            }

            //开始写入使用统计表
            IEnumerable<IGrouping<DateTime, MonthChartRow>> groups = (from f in rows let month = GetYearMonth(f.Date, splitDay) group f by month);
            foreach (IGrouping<DateTime, MonthChartRow> g in groups)
            {
                //int column = 2;
                tmpSheet = workbook.CreateSheet($"{g.Key.Year}年{g.Key.Month}月使用统计");
                List<string> rooms = new List<string>();
                DateTime endDate = new DateTime(g.Key.Year, g.Key.Month, splitDay);
                DateTime startDate = endDate.AddMonths(-1);

                int sumCloumn = (endDate - startDate).Days;
                tmpSheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, sumCloumn + 2));
                tmpSheet.AddMergedRegion(new CellRangeAddress(1, 2, sumCloumn + 1, sumCloumn + 1));
                tmpSheet.AddMergedRegion(new CellRangeAddress(1, 2, sumCloumn + 2, sumCloumn + 2));


                tmpRow = tmpSheet.CreateRow(0);
                tmpCell = tmpRow.CreateCell(0);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue($"{g.Key.Year}年{g.Key.Month}月使用统计");

                tmpRow = tmpSheet.CreateRow(1);
                int column = 1;
                for (DateTime tmpDate = startDate; tmpDate < endDate; tmpDate = tmpDate.AddDays(1))
                {
                    tmpCell = tmpRow.CreateCell(column++);
                    tmpCell.CellStyle = tmpStyle;
                    tmpCell.SetCellValue(tmpDate.Day);
                }
                tmpCell = tmpRow.CreateCell(column++);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue("总学时");

                tmpCell = tmpRow.CreateCell(column++);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue("利用率");

                column = 1;
                tmpRow = tmpSheet.CreateRow(2);
                for (DateTime tmpDate = startDate; tmpDate < endDate; tmpDate = tmpDate.AddDays(1))
                {
                    tmpCell = tmpRow.CreateCell(column++);
                    tmpCell.CellStyle = tmpStyle;
                    tmpCell.SetCellValue(Weeks[(int)tmpDate.DayOfWeek]);
                }

                int row = 3;
                foreach (string room in (from g2 in g select g2.Room).Distinct().OrderBy(g2 => g2))
                {
                    column = 0;
                    tmpRow = tmpSheet.CreateRow(row);
                    tmpCell = tmpRow.CreateCell(column++);
                    tmpCell.CellStyle = tmpStyle;
                    tmpCell.SetCellValue(room);

                    for (DateTime tmpDate = startDate; tmpDate < endDate; tmpDate = tmpDate.AddDays(1))
                    {
                        tmpCell = tmpRow.CreateCell(column++);
                        tmpCell.CellStyle = tmpStyle;
                        int sum = (from g2 in g where g2.Room == room && tmpDate == g2.Date select g2.TimeSpan).Sum();
                        if (sum != 0)
                        {
                            tmpCell.SetCellValue(sum);
                        }
                    }
                    tmpCell = tmpRow.CreateCell(column++);
                    tmpCell.SetCellFormula($"sum({new CellRangeAddress(tmpCell.RowIndex, tmpCell.RowIndex, 1, sumCloumn).FormatAsString() })");
                    tmpCell = tmpRow.CreateCell(column);
                    tmpCell.SetCellFormula($"sum({new CellRangeAddress(tmpCell.RowIndex, tmpCell.RowIndex, sumCloumn + 1, sumCloumn + 1).FormatAsString() }/{sumCloumn * 14})");
                    row++;
                }

            }


            while (true)
            {
                try
                {
                    using (FileStream fs = new FileStream(file, FileMode.Create))
                    {
                        workbook.Write(fs);
                        return true;
                    }
                }
                catch (Exception err)
                {
                    if (MessageBox.Show(this, $"遇到错误:{err.Message},希望重试吗?", "保存错误", MessageBoxButtons.YesNo) == DialogResult.No)
                    {
                        return false;
                    }
                }
            }
        }
    }
}
